package com.caishi.lkx.common.cqhx;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import org.apache.commons.codec.binary.Base64;

import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.HashMap;
import java.util.Map;


/**
 * @Author Duan
 * @Description
 * @Date 2021-1-20
 */
public class ClientDemo {

    public static void main(String[] args) {
        String appSecret = "9268c51dab8d06307f7ef84bdc63e7a1";

        System.out.println("============================<RSA加解密过程>================================");
        System.out.println("client to server.........");
        // 1.内层业务报文加密
        String datagram = clientPublicEncrypt();
        System.out.println("server to client.........");
        // 客户端解密
        clientPublicDecrypt();

        System.out.println("============================<签名值生成过程>================================");
        // 2.组装外层公共报文
        Map<String, String> requestParams = getRequestParams();
        // 3.更新内层业务报文,在本示例中压缩标识统一为0（不压缩）
        requestParams.put("datagram", datagram);
        // 4.对参数进行a-z排序
        String str = sortAndJoin(requestParams);
        System.out.println("拼接后字符串输出：" + str);
        // 5.进行HmacSHA256签名
        HMac mac = new HMac(HmacAlgorithm.HmacSHA256, appSecret.getBytes());
        String signatureStr = mac.digestHex(str.getBytes(StandardCharsets.UTF_8));
        requestParams.put("signature", signatureStr);
        System.out.println("签名值输出：" + signatureStr);
    }

    ////////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////RSA公钥加解密//////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////

    /**
     * <p> 方法描述：公钥加密. </p>
     *
     * @param content    内容
     * @param publicKeyBase64 公钥
     * @return the string
     * @throws NoSuchAlgorithmException 创建key失败
     * @throws InvalidKeySpecException  生成公钥失败
     */
    public static byte[] publicKeyEncrypt(String content, String publicKeyBase64) {
        if (null == content) {
            return null;
        }
        //使用RSA进行加密
        RSA rsa = new RSA(null, publicKeyBase64);
        return rsa.encrypt(content, KeyType.PublicKey);
    }
    /**
     * <p> 方法描述：公钥解密. </p>
     *
     * @param content    内容
     * @param publicKeyBase64 公钥
     * @return the string
     * @throws NoSuchAlgorithmException 创建key失败
     * @throws InvalidKeySpecException  生成公钥失败
     */
    public static String publicKeyDecrypt(byte[] content, String publicKeyBase64) {
        if (null == content || content.length == 0) {
            return "";
        }
        //使用RSA进行加密
        RSA rsa = new RSA(null, publicKeyBase64);
        byte[] bytes = rsa.decrypt(content, KeyType.PublicKey);
        return Convert.hexToStr(Convert.toHex(bytes), CharsetUtil.CHARSET_UTF_8);
    }

    /**
     * 客户端加密流程
     * 加密流程：
     * BASE64.encode(ZIP压缩(RSA加密(内层业务报文json串)))
     */
    public static String clientPublicEncrypt() {
        String zipCode = "0";
        // 1.组装业务报文为json字符串
        String content = getJson();
        System.out.println("客户端加密明文输出：" + content);
        // 2.对业务报文进行RSA加密
        byte[] dataByte = publicKeyEncrypt(content, getPublicKey());
        // 3.判断RSA加密结果是否需要压缩
        if(dataByte.length > 10240) {
            // 需要压缩
            dataByte = ZipUtil.gzip(dataByte);
            zipCode = "1";
        }
        //4. BASE64.encode编码
        dataByte = Base64.encodeBase64(dataByte);
        System.out.println("客户端使用公钥加密输出：" + new String(dataByte));
        return new String(dataByte);
    }

    /**
     * 客户端使用公钥解密
     * 解密流程：
     *  RSA解密(ZIP解压缩(BASE64.decode(密文)))
     */
    public static void clientPublicDecrypt() {
        String zipCode = "0";
        String datagram = "S5NPNB+bQzD6JCIp0jW4I7htFOvC3ebNg5+kC1yOSMHdCJ9rqbF+6HvPVyr2txB9XdSAUrpbyIfIsXCoEvzv4JMw5oa0AVhGhiSOrJL033Ocz9FVkIOZ7j9w1oEXow7VsU13ZgqseG8EJ+LKWM5F9sgzyJQNNniOQ9/C9Lc8Czo=";
        System.out.println("客户端解密密文输出：" + datagram);
        // 1.BASE64.decode解码
        byte[] dataByte = Base64.decodeBase64(datagram);
        if("1".equals(zipCode)) {
            dataByte = ZipUtil.unGzip(dataByte);
        }
        datagram = publicKeyDecrypt(dataByte, getPublicKey());
        System.out.println("客户端使用公钥解密输出：" + datagram);
    }


    /**
     * 对公共请求参数从a-z排序拼接
     * @param messageMap
     * @return
     */
    public static String sortAndJoin(Map<String, String> messageMap) {
        // 将报文数据转换为字符串数组
        String[] keys = messageMap.keySet().toArray(new String[0]);
        // 按照a-z的顺序排序，取消排序
//        Arrays.sort(keys);
        // 拼接报文数据，interfaceCode+zipCode+encryptCode+accessToken+datagram+signtype
        StringBuilder sb = new StringBuilder();
        sb.append(messageMap.get("interfaceCode"));
        sb.append(messageMap.get("zipCode"));
        sb.append(messageMap.get("encryptCode"));
        sb.append(messageMap.get("accessToken"));
        sb.append(messageMap.get("datagram"));
        sb.append(messageMap.get("signtype"));
        return sb.toString();
    }

    /**
     * 组装内层业务报文
     * @return
     */
    public static String getJson() {
        return "{\"a\": \"参数1\",\"b\": \"参数2\",\"c\": \"参数3\",\"d\": [{\"e\": \"参数1\",\"f\": \"参数2\"}]}";
    }

    /**
     * 组装外层公共报文
     * @return
     */
    public static Map<String, String> getRequestParams() {
        // 示例报文数据
        Map<String, String> messageMap = new HashMap<>();
        messageMap.put("interfaceCode", "接口编码");
        messageMap.put("zipCode", "0");
        messageMap.put("encryptCode", "1");
        messageMap.put("accessToken", "身份令牌");
        messageMap.put("taxpayerId", "企业税号");
        messageMap.put("signtype", "HMacSHA256");
        return messageMap;
    }
    /**
     * 私钥
     * @return
     */
    public static String getPublicKey() {
        return "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD+D3GPlRTFZAMErhreo7CNe7Dkhe1esbNHb3BUcvLFDXhmxZOpd/RD7wkTdORAgK+s39oaGfs2C2wiaTio9kPxcReJjMRs+x4JV2EHD+r/I6SJ/a6OY9GvMmxKUrZR5P9u2NtoGiyfhvfWusKd8Etb2nSjD0YbQ4vWBoAUTtQQfQIDAQAB";
    }

}
